package top.kikt.book.core.template

import nl.siegmann.epublib.domain.Author
import nl.siegmann.epublib.domain.Book
import nl.siegmann.epublib.domain.MediaType
import nl.siegmann.epublib.domain.Resource
import nl.siegmann.epublib.epub.EpubWriter
import okhttp3.OkHttpClient
import okhttp3.Request
import top.kikt.book.core.base.IBookHandler
import top.kikt.book.core.entity.BookEntity
import top.kikt.book.core.entity.BookMetaEntity
import java.awt.Color
import java.io.FileOutputStream
import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.io.File
import javax.imageio.ImageIO


class EpubTemplate : OutputTemplate {

    override fun fileExt(): String = "epub"

    override fun write(bookHandler: IBookHandler, entity: BookEntity, outputPath: String): File {
        val bookName = entity.metaEntity.name

        val book = Book()
        val metadata = book.metadata
        metadata.addTitle(bookName)
        metadata.addAuthor(Author(entity.metaEntity.author))

        book.setCover(entity.metaEntity)

        var totalCount = 0

        for (chapterMetaEntity in entity.chapterMetaEntityList) {
            val chapterName = chapterMetaEntity.name
            val index = chapterMetaEntity.index
            val file = entity.getFile(chapterMetaEntity) ?: continue
            val content = readContent(bookHandler, file)

            totalCount += content.replace(" ", "").count()

            val result = makeContent(chapterName, content)
            val res = book.addResource(Resource(result.encodeToByteArray(), "chapters/$index.xhtml"))
            res.mediaType = MediaType("application/xhtml+xml", "xhtml")
            book.addSection(chapterName, res)
        }

        val filePath = getOutputFilePath(bookHandler, entity, outputPath)
        val outputStream = FileOutputStream(filePath)

        outputStream.use {
            val writer = EpubWriter()
            writer.write(book, it)
        }

        val file = File(filePath)
        logger.info("${file.absolutePath} 写入完毕, 共: $totalCount 字")

        return file
    }

    private fun Book.setCover(bookMeta: BookMetaEntity) {

        val bytes = if (bookMeta.coverImageUrl != null) {
            getBytesUrl(bookMeta.coverImageUrl!!) ?: makeCover(bookMeta)
        } else {
            makeCover(bookMeta)
        }

        val res = addResource(Resource("covert", bytes, "cover.png", MediaType("image/png", "png")))
        coverImage = res
    }

    private fun getBytesUrl(coverImageUrl: String): ByteArray? {
        val httpClient = OkHttpClient()

        val request = Request.Builder()
            .get()
            .url(coverImageUrl)
            .build()

        return httpClient.newCall(request)
            .execute()
            .body
            ?.bytes()
    }

    private fun makeCover(bookMeta: BookMetaEntity): ByteArray {
        val name = bookMeta.name
        val author = bookMeta.author

        val img = BufferedImage(210, 297, BufferedImage.TYPE_INT_RGB)
        val graphics = img.graphics
        graphics.color = Color.WHITE
        graphics.clearRect(0, 0, 210, 297)
        graphics.color = Color.BLACK
        graphics.drawString(name, 30, 30)
        graphics.drawString("作者: $author", 50, 99)
        graphics.dispose()

        val outputStream = ByteArrayOutputStream()
        ImageIO.write(img, "jpeg", outputStream)
        return outputStream.toByteArray()
    }

    private fun makeContent(name: String, content: String): String {
        val sb = StringBuilder()

        sb.appendLine(
            """<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN">
<head>
<title>$name</title>
<link href="stylesheet.css" type="text/css" rel="stylesheet"/>
<style type="text/css">
@page { margin-bottom: 5.000000pt; margin-top: 5.000000pt; }
</style>
</head>
<body>"""
        )
        sb.appendLine("<h2>${name}</h2>")

        for (line in content.lines()) {
            if (line.trim().isEmpty()) {
                continue
            }
            sb.appendLine("<p>$line</p>")
        }

        sb.appendLine("</body></html>")

        return sb.toString()
    }
}
